﻿//checked
using net.entity.framework.common;

namespace net.entity.framework.query {
    public abstract class BaseBuilder {
        #region Members

        protected readonly QueryState queryState;

        #endregion

        #region Properties

        public QueryState QueryState {
            get { return queryState; }
        }

        #endregion

        #region Constructor

        protected BaseBuilder(bool distinct, int top, QueryRoot[] selectRoots, QueryBuilder[] nestedFroms,
                              QueryClause whereClause, Terminal[] groupByRoots,
                              QueryClause havingClause, Terminal[] orderByRoots) {
            queryState = new QueryState(selectRoots, distinct, top, whereClause,
                                        groupByRoots, havingClause, nestedFroms, orderByRoots);
        }

        #endregion

        #region Methods

        public string ToCommandText() {
            var compiler = GetCompiler();
            var output = compiler.CompileAndRun(queryState);
            Utils.WriteToLog(output);
            return output;
        }

        #endregion

        protected abstract QueryCompiler GetCompiler();
    }

    public class QueryBuilder : BaseBuilder {
        #region Constructors

        protected QueryBuilder(bool distinct, int top, QueryRoot[] selectRoots, QueryBuilder[] nestedFroms,
                               QueryClause whereClause, Terminal[] groupByRoots,
                               QueryClause havingClause, Terminal[] orderByRoots)
            : base(distinct, top, selectRoots, nestedFroms, whereClause, groupByRoots, havingClause, orderByRoots) {}

        #endregion

        #region Builders

        /// <summary>
        /// Adds Order By
        /// </summary>
        public class OrderBuilder : QueryBuilder {
            public OrderBuilder(bool distinct, int top, QueryRoot[] selectRoots, QueryBuilder[] nestedFroms,
                                QueryClause whereClause, Terminal[] groupByRoots,
                                QueryClause havingClause)
                : base(distinct, top, selectRoots, nestedFroms, whereClause, groupByRoots,
                       havingClause, Terminal.EmptyTerminals) {}

            public QueryBuilder OrderBy(params Terminal[] newOrderByRoots) {
                return new QueryBuilder(queryState.Distinct, queryState.Top, queryState.SelectRoots,
                                        queryState.NestedFroms, queryState.WhereClause,
                                        queryState.GroupByRoots, queryState.HavingClause, newOrderByRoots);
            }
        }

        /// <summary>
        /// Adds having
        /// </summary>
        public class HavingBuilder : OrderBuilder {
            public HavingBuilder(bool distinct, int top, QueryRoot[] selectRoots, QueryBuilder[] nestedFroms,
                                 QueryClause whereClause, Terminal[] groupByRoots)
                : base(distinct, top, selectRoots, nestedFroms, whereClause, groupByRoots,
                       BooleanConstant.True) {}

            public OrderBuilder Having(QueryClause newHavingQueryClause) {
                return new OrderBuilder(queryState.Distinct, queryState.Top, queryState.SelectRoots,
                                        queryState.NestedFroms, queryState.WhereClause,
                                        queryState.GroupByRoots, newHavingQueryClause);
            }
        }

        /// <summary>
        /// Adds group by
        /// </summary>
        public class GroupBuilder : HavingBuilder {
            public GroupBuilder(bool distinct, int top, QueryRoot[] selectRoots, QueryBuilder[] nestedFroms,
                                QueryClause whereClause)
                : base(distinct, top, selectRoots, nestedFroms, whereClause, Terminal.EmptyTerminals) {}

            public HavingBuilder GroupBy(params Terminal[] newGroupByRoots) {
                return new HavingBuilder(queryState.Distinct, queryState.Top, queryState.SelectRoots,
                                         queryState.NestedFroms, queryState.WhereClause,
                                         newGroupByRoots);
            }
        }

        /// <summary>
        /// Adds where
        /// </summary>
        public class WhereBuilder : GroupBuilder {
            public WhereBuilder(bool distinct, int top, QueryRoot[] selectRoots, QueryBuilder[] nestedFroms)
                : base(distinct, top, selectRoots, nestedFroms, BooleanConstant.True) {}

            public GroupBuilder Where(QueryClause newWhereQueryClause) {
                return new GroupBuilder(queryState.Distinct, queryState.Top, queryState.SelectRoots,
                                        queryState.NestedFroms, newWhereQueryClause);
            }
        }

        /// <summary>
        /// Adds from
        /// </summary>
        public class BuilderBottom : WhereBuilder {
            public BuilderBottom(bool distinct, int top, QueryRoot[] selectRoots)
                : base(distinct, top, selectRoots, null) {}

            public WhereBuilder From(params QueryBuilder[] newNestedFroms) {
                return new WhereBuilder(queryState.Distinct, queryState.Top, queryState.SelectRoots, newNestedFroms);
            }
        }

        #endregion

        public QueryContext GetQueryContext() {
            return new QueryContext(queryState);
        }

        protected override QueryCompiler GetCompiler() {
            return new SelectQueryCompiler(queryState);
        }
    }
}